package globalLogger

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"io"
	"net/http"
	"os"
	"path"
	"runtime/debug"
	"time"
)

/*
init
用于包的初始化。init() 函数在包被引入后会被自动执行。如果在 main 包中，它也会在 main() 函数之前执行。
2006-01-02 15:04:05 是 Go 语言的诞生时间，这个时间是固定的，不会改变。 代表着年月日时分秒。格式化时间的时候，需要用到这个时间。
*/
func init() {
	logrus.SetFormatter(&logrus.JSONFormatter{
		TimestampFormat: "2006-01-02 15:04:05",
	})
	logrus.SetReportCaller(false)
}

func Write(msg string, filename string) {
	setOutPutFile(logrus.InfoLevel, filename)
	logrus.Info(msg)
}

func Debug(fields logrus.Fields, args ...interface{}) {
	setOutPutFile(logrus.DebugLevel, "debug")
	logrus.WithFields(fields).Debug(args)
}

func Info(fields logrus.Fields, args ...interface{}) {
	setOutPutFile(logrus.InfoLevel, "info")
	logrus.WithFields(fields).Info(args)
}

func Warn(fields logrus.Fields, args ...interface{}) {
	setOutPutFile(logrus.WarnLevel, "warn")
	logrus.WithFields(fields).Warn(args)
}

func Fatal(fields logrus.Fields, args ...interface{}) {
	setOutPutFile(logrus.FatalLevel, "fatal")
	logrus.WithFields(fields).Fatal(args)
}

func Error(fields logrus.Fields, args ...interface{}) {
	setOutPutFile(logrus.ErrorLevel, "error")
	fmt.Println(fields)
	logrus.WithFields(fields).Error(args)
}

/*
setOutPutFile 函数用于设置日志输出文件。
1. setOutPutFile 函数接收两个参数，level 和 logName，分别表示日志级别和日志文件名。
2. level 参数是 logrus.Level 类型的值，表示日志级别。
3. logName 参数是 string 类型的值，表示日志文件名。
4. setOutPutFile 函数首先判断 ./runtime/log 目录是否存在，如果不存在则创建该目录。
5. 然后，setOutPutFile 函数根据 logName 和当前日期生成日志文件名。
6. 最后，setOutPutFile 函数将日志输出到指定的文件中，并设置日志级别。
*/
func setOutPutFile(level logrus.Level, logName string) {
	if _, err := os.Stat("./runtime/log"); os.IsNotExist(err) {
		err = os.MkdirAll("./runtime/log", 0777)
		if err != nil {
			panic(fmt.Errorf("create log dir '%s' error:%s", "./runtime/log", err))
		}
	}

	timeStr := time.Now().Format("2006-01-02")
	fileName := path.Join("./runtime/log", logName+"_"+timeStr+".log")

	var err error

	os.Stderr, err = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)

	if err != nil {
		fmt.Println("open log file err", err)
	}

	logrus.SetOutput(os.Stderr)
	logrus.SetLevel(level)

	return
}

/*
RecordToFile 函数用于记录日志到文件。
1. RecordToFile 函数返回 gin.LoggerConfig 类型的值，该值包含了记录日志到文件的配置信息。
2. gin.LoggerConfig 是 Gin 框架的日志记录配置，它包含了日志记录的格式、输出位置等信息。
3. Formatter 字段是一个函数，用于格式化日志记录。
4. Output 字段是一个 io.Writer 接口类型的值，用于指定日志输出位置。
5. Formatter 字段是一个函数，用于格式化日志记录。
*/
func RecordToFile() gin.LoggerConfig {
	if _, err := os.Stat("./runtime/log"); os.IsNotExist(err) {
		err = os.MkdirAll("./runtime/log", 0777)
		if err != nil {
			panic(fmt.Errorf("create log dir '%s' error:%s", "./runtime/log", err))
		}
	}

	timeStr := time.Now().Format("2006-01-02")
	fileName := path.Join("./runtime/log", "success_"+timeStr+".log")

	os.Stderr, _ = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	var config = gin.LoggerConfig{
		Formatter: func(params gin.LogFormatterParams) string {
			return fmt.Sprintf("%s - %s \"%s %s %s %d \"%s\" %s\"%s\"\n",
				params.TimeStamp.Format("2006-01-02 15:04:06"),
				params.ClientIP,
				params.Method,
				params.Path,
				params.Request.Proto,
				params.StatusCode,
				params.Latency,
				params.Request.UserAgent(),
				params.ErrorMessage,
			)
		},
		Output: io.MultiWriter(os.Stdout, os.Stderr),
	}
	fmt.Println(config)
	return config

}

/*
Recover 函数用于处理异常。
<br>1. 函数接收一个 gin.Context 类型的参数 c，表示 Gin 框架的上下文对象。</br>
2. 函数使用 defer 语句延迟执行匿名函数。
3. 匿名函数中使用 recover 函数捕获异常。
4. 如果捕获到异常， 函数将异常信息写入日志文件，并返回异常信息给客户端。
5. 最后，Recover 函数调用 c.Next() 方法，继续执行后续代码。
*/
func Recover(routerServer *gin.Context) {
	defer func() {
		if err := recover(); err != nil {
			if _, errDir := os.Stat("./runtime/log"); os.IsNotExist(errDir) {
				err = os.MkdirAll("./runtime/log", 0777)
				if err != nil {
					panic(fmt.Errorf("create log dir '%s' error:%s", "./runtime/log", err))
				}
			}

			timeStr := time.Now().Format("2006-01-02")
			fileName := path.Join("./runtime/log", "err_"+timeStr+".log")
			f, errFile := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
			if errFile != nil {
				fmt.Println(errFile)
			}
			timeFileStr := time.Now().Format("2006-01-02 15:04:05")
			_, err2 := f.WriteString("panic error time:" + timeFileStr + "\n")
			println(timeFileStr)
			if err2 != nil {
				return
			}
			_, err := f.WriteString(fmt.Sprintf("%v", err) + "\n")
			if err != nil {
				return
			}
			_, err = f.WriteString("stacktrace from panic:" + string(debug.Stack()) + "\n")
			if err != nil {
				return
			}
			err = f.Close()
			if err != nil {
				return
			}
			routerServer.JSON(http.StatusOK, gin.H{"code": 500, "msg": fmt.Sprintf("%v", err)})
			//终止后续接口调用，不加的话recover异常之后，还会继续执行后续代码
			routerServer.Abort()
		}
	}()
	routerServer.Next()
}
